/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */

package recommender.services;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;
import org.json.simple.JSONObject;
import recommender.controller.PersonalRecommender;
import recommender.objects.Context;
import recommender.objects.User;
import recommender.objects.Video;

/**
 *
 * @author Aya
 */
@Path("service")
public class WebServices {
    
    Gson gson = new GsonBuilder().enableComplexMapKeySerialization().create();
    
    @GET
    @Path("/test")
    public Response test(){
        return Response.status(200).entity("Ya ahlan wa sahlan!").header("Access-Control-Allow-Origin", "*").build();
    }
    
    @GET
    @Path("/nodes")
    @Produces("application/json")
    public Response getTopNodes(){
        return Response.status(200)
                .entity(gson.toJson(PersonalRecommender.getTopNodes()))
                .header("Access-Control-Allow-Origin", "*").build();
    }
    
    @GET
    @Path("/videos/{keyword}/{mode}")
    @Produces("application/json")
    public Response getVideosAbout(@PathParam("keyword") String keyword, @PathParam("mode") int mode){
        return Response.status(200)
                .entity(gson.toJson(PersonalRecommender.vbi.searchVideos(keyword, mode)))
                .header("Access-Control-Allow-Origin", "*").build();
    }
    
    @GET
    @Path("/videos")
    @Produces("application/json")
    public Response getAllVideos(){
        return Response.status(200)
                .entity(gson.toJson(PersonalRecommender.getAllVideos()))
                .header("Access-Control-Allow-Origin", "*").build();
    }
    
    @GET
    @Path("/build")
    @Produces("application/json")
    public Response build(){
        JSONObject jo = new JSONObject();
        long start = System.currentTimeMillis();
        PersonalRecommender.buildKB("LUMOPedia Knowledge Base");
        long end = System.currentTimeMillis();
        jo.put("KB", (end-start)+"ms");
        /* Build the video base (only once) and knowledge base needed in advance */
        start = System.currentTimeMillis();
        PersonalRecommender.buildVB("LTV Video Base", 0);
        end = System.currentTimeMillis();
        jo.put("VB", (end-start)+"ms");
        start = System.currentTimeMillis();
        PersonalRecommender.buildGroupVB("LTV Group Video Base 2", 2);
        end = System.currentTimeMillis();
        jo.put("GB_2", (end-start)+"ms");
        start = System.currentTimeMillis();
        PersonalRecommender.buildGroupVB("LTV Group Video Base 3", 3);
        end = System.currentTimeMillis();
        jo.put("GB_3", (end-start)+"ms");
        return Response.status(200).entity(jo.toJSONString()).header("Access-Control-Allow-Origin", "*").build();
    }
    
    @POST
    @Path("/user")
    @Consumes("application/x-www-form-urlencoded")
    public Response createUser(MultivaluedMap<String, String> formParams){
        try {
            String uname = formParams.getFirst("username");
            String pwd = formParams.getFirst("password");
            String full_name = formParams.getFirst("name");
            String country = formParams.getFirst("country");
            SimpleDateFormat df = new SimpleDateFormat("dd-MM-yyyy");
            Date dob = df.parse(formParams.getFirst("dob"));
            if(uname == null || pwd == null || full_name == null || country == null || dob == null)
                return Response.status(400)
                    .entity("")
                    .header("Access-Control-Allow-Origin", "*").build();
            
            return Response.status(200)
                .entity(PersonalRecommender.ubi.createUser(uname, pwd, full_name, country, dob))
                .header("Access-Control-Allow-Origin", "*").build();
            
        } 
        catch (ParseException ex) {
            ex.printStackTrace();
            return Response.status(500)
                .entity("")
                .header("Access-Control-Allow-Origin", "*").build();
        }
    }
    
    @GET
    @Path("/validate/{username}")
    public Response validateUnique(@PathParam("username") String username){
        return Response.status(200)
                .entity(PersonalRecommender.ubi.isValid(username))
                .header("Access-Control-Allow-Origin", "*").build();
    }
    
    @POST
    @Path("/logout/{u}")
    public Response logout(@PathParam("u") int uid){
        PersonalRecommender.logout(uid);
        return Response.status(200)
                .entity("")
                .header("Access-Control-Allow-Origin", "*").build();
    }
    
    @POST
    @Path("/login")
    @Consumes("application/x-www-form-urlencoded")
    @Produces("application/json")
    public Response login(MultivaluedMap<String, String> formParams){
        String uname = formParams.getFirst("username");
        String pwd = formParams.getFirst("password");
        if(uname == null || pwd == null){
            return Response.status(400)
                .header("Access-Control-Allow-Origin", "*").build();
        }
        User u = PersonalRecommender.login(uname, pwd);
        if(u == null){ // no user with this info
            return Response.status(404)
                .entity("User not found")
                .header("Access-Control-Allow-Origin", "*").build();
        }
        return Response.status(200)
                .entity(gson.toJson(u))
                .header("Access-Control-Allow-Origin", "*").build();
    }
    
    @POST
    @Path("/preferences")
    @Consumes("application/x-www-form-urlencoded")
    @Produces("application/json")
    public Response userPreferences(MultivaluedMap<String, String> formParams){
        int uid = Integer.parseInt(formParams.getFirst("uid"));
        boolean t = Boolean.parseBoolean(formParams.getFirst("use_threshold"));
        int v_threshold = Integer.parseInt(formParams.getFirst("vt"));
        int e_threshold = Integer.parseInt(formParams.getFirst("et"));
        int nvid = Integer.parseInt(formParams.getFirst("nv"));
        int nint = Integer.parseInt(formParams.getFirst("ne"));
        PersonalRecommender.updatePref(uid, t, v_threshold, e_threshold, nvid, nint);
        return Response.status(200)
                .entity("")
                .header("Access-Control-Allow-Origin", "*").build();
    }
    
    @GET
    @Path("/load/{u}")
    @Produces("application/json")
    public Response loadUser(@PathParam("u") int u){
        JSONObject jo = new JSONObject();
        jo.put("recommendations", gson.toJson(PersonalRecommender.fetchRecommendations(u)));
        jo.put("entities", gson.toJson(PersonalRecommender.fetchInferredInterests(u)));
        return Response.status(200)
                .entity(jo.toJSONString())
                .header("Access-Control-Allow-Origin", "*").build();
    }
    
    @GET
    @Path("/recommend/videos/{u}")
    @Produces("application/json")
    public Response recommendVideos(@PathParam("u") int u){
        return Response.status(200)
                .entity(gson.toJson(PersonalRecommender.fetchRecommendations(u)))
                .header("Access-Control-Allow-Origin", "*").build();
    }
    
    @GET
    @Path("/recommend/group/videos")
    @Produces("application/json")
    public Response recommendGroupVideos(@QueryParam("u") String u){
        String[] users = u.split(",");
        List<Integer> group = new ArrayList<>();
        for(String user: users){
            group.add(Integer.parseInt(user));
        }
        Map<Video, Double> fetchedRecommendations = PersonalRecommender.fetchRecommendations(group);
        if(fetchedRecommendations != null){
            return Response.status(200)
                    .entity(gson.toJson(fetchedRecommendations))
                    .header("Access-Control-Allow-Origin", "*").build();
        } else{
            return Response.status(400)
                    .entity("Currently, the maximum group size supported is 3.")
                    .header("Access-Control-Allow-Origin", "*").build();
        }
    }
    
    @GET
    @Path("/recommend/entities/{u}")
    @Produces("application/json")
    public Response recommendEntities(@PathParam("u") int u){
        return Response.status(200)
                .entity(gson.toJson(PersonalRecommender.fetchInferredInterests(u)))
                .header("Access-Control-Allow-Origin", "*").build();
    }
    
    @GET
    @Path("/history/likevideos/{u}/{n}}")
    @Produces("application/json")
    public Response getLiked(@PathParam("n") int n, @PathParam("u") int u){
        List<Video> vids = PersonalRecommender.ubi.getLikeVideos(u);
        if(vids.size() > n){
            return Response.status(200)
                .entity(gson.toJson(vids.subList(0, n)))
                .header("Access-Control-Allow-Origin", "*").build();
        }
        else {
            return Response.status(200)
                .entity(gson.toJson(vids))
                .header("Access-Control-Allow-Origin", "*").build();
        }
    }
    
    @GET
    @Path("/history/dislikevideos/{u}/{n}}")
    @Produces("application/json")
    public Response getDisliked(@PathParam("n") int n, @PathParam("u") int u){
        List<Video> vids = PersonalRecommender.ubi.getDislikeVideos(u);
        if(vids.size() > n){
            return Response.status(200)
                .entity(gson.toJson(vids.subList(0, n)))
                .header("Access-Control-Allow-Origin", "*").build();
        }
        else {
            return Response.status(200)
                .entity(gson.toJson(vids))
                .header("Access-Control-Allow-Origin", "*").build();
        }
    }
    
    @GET
    @Path("/history/interest/{u}}")
    @Produces("application/json")
    public Response getInterest(@PathParam("u") int u){
        return Response.status(200)
                .entity(gson.toJson(PersonalRecommender.ubi.getInterests(u)))
                .header("Access-Control-Allow-Origin", "*").build();
    }
    
    @GET
    @Path("/history/disinterest/{u}}")
    @Produces("application/json")
    public Response getDisinterest(@PathParam("u") int u){
        return Response.status(200)
                .entity(gson.toJson(PersonalRecommender.ubi.getDisinterests(u)))
                .header("Access-Control-Allow-Origin", "*").build();
    }
    
    @POST
    @Path("/vfeedback/{u}/{c}/{v}/{l}")
    public Response setVideoFeedback(@PathParam("u") int uid, @PathParam("v") int vid, 
            @PathParam("l") boolean like, @PathParam("c") int cid){
        try {
            PersonalRecommender.setLike(uid, cid, vid, like);
            return Response.status(200)
                .entity("")
                .header("Access-Control-Allow-Origin", "*").build();
            
        } catch(Exception e){
            e.printStackTrace();
            return Response.status(500)
                .entity(e.getMessage())
                .header("Access-Control-Allow-Origin", "*").build();
        }
    }
    
    @POST
    @Path("/ifeedback/{u}/{c}/{e}/{l}")
    public Response setInterestFeedback(@PathParam("u") int uid, @PathParam("e") String eid, 
            @PathParam("l") boolean like, @PathParam("c") int cid){
        try {
            PersonalRecommender.setInterest(uid, cid, eid, like);
            return Response.status(200)
                .entity("")
                .header("Access-Control-Allow-Origin", "*").build();
            
        } catch(Exception e){
            e.printStackTrace();
            return Response.status(500)
                .entity(e.getMessage())
                .header("Access-Control-Allow-Origin", "*").build();
        }
    }
    
    @POST
    @Path("/forget/{u}")
    public Response forget(@PathParam("u") int uid){
        try {
            PersonalRecommender.forgetUser(uid);
            return Response.status(200)
                .entity("")
                .header("Access-Control-Allow-Origin", "*").build();
            
        } catch(Exception e){
            e.printStackTrace();
            return Response.status(500)
                .entity(e.getMessage())
                .header("Access-Control-Allow-Origin", "*").build();
        }
    }
    
    @GET
    @Path("/annotations/{v}")
    public Response getAnnotations(@PathParam("v") int vid){
        try {
            return Response.status(200)
                    .entity(gson.toJson(PersonalRecommender.getAnnotations(vid)))
                    .header("Access-Control-Allow-Origin", "*").build();
        } catch(Exception e){
            e.printStackTrace();
            return Response.status(500)
                    .entity(e.getMessage())
                    .header("Access-Control-Allow-Origin", "*").build();
        }
    }
    
    @GET
    @Path("/ctx/{u}")
    public Response getContextTree(@PathParam("u") int uid){
        try {
        List<Context> c = PersonalRecommender.ubi.getContexts(uid);
        return Response.status(200)
                    .entity(gson.toJson(c))
                    .header("Access-Control-Allow-Origin", "*").build();
        } catch(Exception e){
            e.printStackTrace();
            return Response.status(500)
                    .entity(e.getMessage())
                    .header("Access-Control-Allow-Origin", "*").build();
        }
    }
    
    @GET
    @Path("/ctx/{c}/{n}")
    public Response addContext(@PathParam("c") int cid, @PathParam("n") String n){
        try {
        int id = PersonalRecommender.addContext(cid, n);
        return Response.status(200)
                    .entity(id)
                    .header("Access-Control-Allow-Origin", "*").build();
        } catch(Exception e){
            e.printStackTrace();
            return Response.status(500)
                    .entity(e.getMessage())
                    .header("Access-Control-Allow-Origin", "*").build();
        }
    }
    
    @POST
    @Path("/ctx/rename/{cid}/{cname}")
    public Response renameContext(@PathParam("cid") int cid, @PathParam("cname") String cname){
        try {
        PersonalRecommender.ubi.renameContext(cid, cname);
        return Response.status(200)
                    .entity("")
                    .header("Access-Control-Allow-Origin", "*").build();
        } catch(Exception e){
            e.printStackTrace();
            return Response.status(500)
                    .entity(e.getMessage())
                    .header("Access-Control-Allow-Origin", "*").build();
        }
    }
    
    @POST
    @Path("/ctx/{u}/{c}")
    public Response setContext(@PathParam("u") int uid, @PathParam("c") int cid){
        try {
        PersonalRecommender.updateContext(uid, cid);
        return Response.status(200)
                    .entity("")
                    .header("Access-Control-Allow-Origin", "*").build();
        } catch(Exception e){
            e.printStackTrace();
            return Response.status(500)
                    .entity(e.getMessage())
                    .header("Access-Control-Allow-Origin", "*").build();
        }
    }
}
